﻿using System;
using EnvDTE;
using EnvDTE80;

namespace PeterKuhn.SilverlightDebugHelper
{
    /// <summary>
    /// An extension container class.
    /// </summary>
    internal static class Extensions
    {
        /// <summary>
        /// Gets the assembly name of the project.
        /// </summary>
        /// <param name="project">The project to inspect.</param>
        /// <returns>The aseembly name of the project or null if it could not be determined.</returns>
        public static string GetProjectAssemblyName(this Project project)
        {
            if (project == null)
            {
                return null;
            }

            if (project.Properties == null)
            {
                return null;
            }

            var item = project.Properties.Item("AssemblyName");
            if (item == null)
            {
                return null;
            }

            var result = item.Value.ToString();
            return result;
        }

        /// <summary>
        /// Determines whether a debugger is already attached to a process.
        /// </summary>
        /// <param name="debugger">The debugger to inspect.</param>
        /// <param name="processId">The process id to check for.</param>
        /// <returns>
        ///   <c>true</c> if the debugger is already attached to the process with the given id; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsDebuggingProcess(this Debugger debugger, int processId)
        {
            if (debugger == null)
            {
                return false;
            }

            if (debugger.DebuggedProcesses == null)
            {
                return false;
            }

            try
            {
                foreach (Process debuggedProcess in debugger.DebuggedProcesses)
                {
                    if (debuggedProcess.ProcessID == processId)
                    {
                        return true;
                    }
                }

                return false;
            }
            catch (Exception ex)
            {
                (debugger.DTE as DTE2).OutputFormattedDebugText("*** Exception while trying to figure out whether a certain process is already being debugged: " + ex.ToString());
                return false;
            }
        }

        /// <summary>
        /// Determines whether the specified process has loaded a certain module.
        /// </summary>
        /// <param name="process">The process to inspect.</param>
        /// <param name="name">The name of the module to check for.</param>
        /// <returns>
        ///   <c>true</c> if the specified process has loaded the module; otherwise, <c>false</c>.
        /// </returns>
        public static bool HasLoadedModule(this Process process, string name)
        {
            if (process == null)
            {
                return false;
            }

            try
            {
                string upperName = name.ToUpper();

                System.Diagnostics.Process diagnosticsProcess = System.Diagnostics.Process.GetProcessById(process.ProcessID);

                foreach (System.Diagnostics.ProcessModule module in diagnosticsProcess.Modules)
                {
                    string upperModuleName = module.ModuleName.ToUpper();

                    if (upperModuleName.Contains(upperName))
                    {
                        return true;
                    }
                }

                return false;
            }
            catch (Exception ex)
            {
                (process.DTE as DTE2).OutputFormattedDebugText("*** Exception while trying to figure out whether a certain process has loaded a module: " + ex.ToString());
                return false;
            }
        }

        /// <summary>
        /// Waits for the input idle state of a process.
        /// </summary>
        /// <param name="process">The process in question.</param>
        public static void WaitForInputIdle(this Process process)
        {
            if (process == null)
            {
                return;
            }

            try
            {
                System.Diagnostics.Process diagnosticsProcess = System.Diagnostics.Process.GetProcessById(process.ProcessID);
                if (!diagnosticsProcess.WaitForInputIdle(5000))
                {
                    (process.DTE as DTE2).OutputFormattedDebugText("Wait for input idle of a process failed after 5000 milliseconds.");
                }
            }
            catch (Exception ex)
            {
                (process.DTE as DTE2).OutputFormattedDebugText("*** Exception while trying to wait for input idle of a process: " + ex.ToString());
            }
        }

        /// <summary>
        /// Outputs text to the debug window of Visual Studio.
        /// </summary>
        /// <param name="application">The application that represents Visual Studio.</param>
        /// <param name="message">The message to output.</param>
        public static void OutputDebugText(this DTE2 application, string message)
        {
            try
            {
                Window outputToolWindow = application.Windows.Item(EnvDTE.Constants.vsWindowKindOutput);
                OutputWindow outputWindow = outputToolWindow.Object as OutputWindow;
                OutputWindowPane debugPane = null;

                foreach (OutputWindowPane pane in outputWindow.OutputWindowPanes)
                {
                    if (pane.Guid.ToUpper() == PeterKuhn.SilverlightDebugHelper.Constants.DebugOutputWindowPaneGuid)
                    {
                        debugPane = pane;
                        break;
                    }
                }

                if (debugPane != null)
                {
                    if (!message.EndsWith(Environment.NewLine))
                    {
                        message += Environment.NewLine;
                    }

                    debugPane.OutputString(message);
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine(message);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("*** Error while writing to debug output window: " + ex);
            }
        }

        /// <summary>
        /// Outputs formatted debug text to the debug window of Visual Studio.
        /// </summary>
        /// <param name="application">The application that represents Visual Studio.</param>
        /// <param name="message">The message to output.</param>
        public static void OutputFormattedDebugText(this DTE2 application, string message)
        {
            const string format = "{0} [{1}]: {2}";
            string formattedMessage = string.Format(format, Constants.ApplicationName, DateTime.Now.ToString("o"), message);
            application.OutputDebugText(formattedMessage);
        }
    }
}
